
#include <iostream>

class Base {

public:
    uint64_t num = 1;

    virtual void show_num(void) 
    {
        std::cout << "base show_num: " << num << std::endl;
    }

    virtual void show_size(void)
    {
        std::cout << "base show_size: " << sizeof(Base) << std::endl;
    }

};

class Der: public Base
{
public:
    virtual void show_num(void) override
    {
        std::cout << "derived show_num: " << num << std::endl;
    }
    virtual void show_size(void) override
    {
        std::cout << "derived show_size: " << sizeof(Der) << std::endl;
    }
};

template<typename DstType, typename SrcType>
DstType union_cast(SrcType data)
{
    static_assert(sizeof(DstType) >= sizeof(SrcType), "src type must match dst type!");
    union
    {
        DstType d;
        SrcType f;
    }u{};
    u.f = data;
    return u.d;
}

struct uint128
{
    uint64_t u64[2];
};

template<typename Ret, class T, class... Args>
uint64_t getoff(uint64_t *v_table,  Ret(T::*func)(Args ...))
{
    uint64_t* ptr = v_table;
    static_assert(sizeof(void *) == sizeof(uint64_t), "only surpport 64bit machine");
    auto addr = union_cast<uint128>(func);
    for(; *ptr != addr.u64[0] && *ptr != addr.u64[1]; ptr ++)
    {
    }
    return (uint64_t) ptr - (uint64_t)v_table;
}

template <typename T> void print_data(T *A)
{
    printf("type: %s\n", typeid(T).name());
    printf("vtable addr     : %016llX\n", *(uint64_t *)A);
    printf("vtable [0]      : %016llX\n", (*(uint64_t **)A)[0]);
    printf("vtable [1]      : %016llX\n", (*(uint64_t **)A)[1]);
    printf("base addr       : %016llX\n", (uint64_t)A);
    printf("num  addr       : %016llX\n", (uint64_t)(&A->num));
    printf("show_num addr   : %p\n", &T::show_num);
    printf("show_size addr  : %p\n", &T::show_size);
    printf("show_num off    : %llu\n", getoff(*(uint64_t **)A, &T::show_num));
    printf("show_size off   : %llu\n", getoff(*(uint64_t **)A, &T::show_size));

    printf("\n");
}

int main()
{
    printf("warnning: sizeof member function ptr is %llu\n", sizeof(decltype(&Base::show_size)));
    Base A1, A2;
    Der B1, B2;
    print_data(&A1);
    print_data(&A2);
    print_data(&B1);
    print_data(&B1);

    return 0;
}

